home *** CD-ROM | disk | FTP | other *** search
- ---FLOAT.DOC---
-
- In this file, we'll refer to the various Central Processing Units (CPUs)
- as the "86". Thus "86" refers to either the 8088, 8086, 80186, 80286, etc.
- We'll refer to the various coprocessors as the "87". Thus "87" refers to
- either the 8087 or the 287.
-
-
- The 8087 and 287 Coprocessors
-
- All IBM-PC's, and most clones, contain a socket for a floating-point coprocesor.
- If you shell out between $80 and $400, and plug the appropriate chip into that
- socket, then a host of floating-point instructions is added to the assembly-
- language instruction set.
-
- The original IBM-PC, and the XT, accept the original floating-point chip, the
- 8087. The AT accepts a later update, the 287. From a programming standpoint,
- the two chips are nearly identical: the 287 adds the instructions FSETPM and
- FSTSW AX, and ignores the instructions FENI and FDISI. There is, however, a
- rather nasty design flaw in the 8087, that was corrected in the 287.
-
- To understand the flaw, you must understand how the 86 and 87 work as
- coprocessors. Whenever the 86 sees a floating-point instruction, it
- communicates the instruction, and any associated memory operands, to the 87.
- Then the 86 goes on to its next instruction, operating in parallel with the 87.
- That's OK, so long as the following instructions don't do one of the following:
-
- 1. Execute another floating point instruction; or
- 2. Try to read the results of the still-executing floating point instruction.
-
- If they do, then you must provide an instruction called WAIT (or synonymously
- FWAIT), which halts the 86 until the 87 is finished. For almost all
- floating-point instructions, it should not be necessary to provide an explicit
- FWAIT; the 86 ought to know that it should wait. For the 8087, it IS necessary
- to give an explicit FWAIT before each floating-point instruction: that is the
- flaw.
-
- Because of the flaw, all assemblers supporting the 8087 will silently insert
- an FWAIT code (hex 9B) before all 87 instructions, except those few
- (the FN-instructions other than FNOP) not requiring the FWAIT. A86 provides the
- directive ".287", compatible with Microsoft's assembler, to signal that the
- 287 is the target processor. However, the actions taken by A86 and Microsoft
- when seeing .287 are completely disjoint! To wit:
-
- * A86 ceases outputting FWAIT directives that are unneccary for the 287. For
- reasons beyond my comprehension, Microsoft continues to put them out. I
- wrote the 286/287 assembly language manual for Intel, so I'm pretty confident
- that those codes aren't needed. But I haven't bought a 287 and actually
- tested the assertion. Can someone enlighten me as to why Microsoft is
- putting out those codes?
-
- * A86 ignores the instructions FENI, FDISI, FNENI, and FNDISI after it sees
- a .287 directive. Microsoft continues to assemble these instructions.
-
- * Microsoft recognizes the new 287 instructions, if and only if it sees the
- .287 directive. A86 recognizes them even if .287 is not given. In general,
- I don't attempt to police your instruction usage-- if you use an instruction
- available on a limited number of processors, I trust that you are programming
- for one of those processors.
-
- In summary, if you have an AT with a 287, you should try the ".287" directive,
- and see if your programs still work. If they do, leave the directive in: your
- programs will be significantly shorter than if they were assembled by
- Microsoft. If your programs don't work under ".287", take away the 287, and
- let me know about it.
-
-
- The Floating Point Stack
-
- The 87 has its own register set, of 8 floating-point numbers occupying 10 bytea
- each, plus 14 bytes of status and control information. Many of the 87's
- instructions cause the numbers to act like a stack, much like a Hewlett-
- Packard calculator. For this reason, the numbers are called the floating-point
- stack. The standard name for the top element of the floating-point stack is
- either ST or ST(0); the others are named ST(1) through ST(7). Thus, for
- example, the instruction to add stack element number 3 into the top stack
- element is usually coded FADD ST,ST(3).
-
- I find this notation painfully verbose. Especially bad are the parentheses,
- which are hard to type, and which add visual clutter to the program. To
- alleviate this problem while retaining language compatibility, I name my
- stack elements simply 0 through 7. I recognize ST as a synonym for 0.
- I allow expression elements to be concatenated; concatenation is the same as
- addition. Thus, when A86 sees ST(3), it computes 0+3 = 3. So you can code
- the old way, FADD ST,ST(3), or you can code the concise way, FADD 0,3 or
- simply FADD 3.
-
-
-
- Floating Point Operand Types
-
- The list of floating point instructions contain a variety of operand types.
- Here is a brief explanation of those types:
-
- 0 stands for the top element of the floating-point stack. A synonym for 0
- is ST or ST(0).
-
- i stands for element number i of the floating-point stack. i can range from
- 0 through 7. A synonym for i is ST(i).
-
- mem10r is a 10-byte memory quantity (typically declared with a DT directive)
- containing a full-precision floating point number. Intel recommends that
- you NOT store your numbers in full precision; that you use the following
- double precision format instead. Full-precision numbers are intended for
- storage of intermediate results (on the stack); they exist to insure
- maximum accuracy for calculations on double-precision numbers, which is the
- official external format of 87 numbers.
-
- mem8r is an 8-byte memory quantity (typically declared with a DQ directive)
- containing a double-precision floating-point number. This is the best
- format for floating-point numbers on the 87. The 87 takes the same amount
- of time on double-precision calculations as it does on single-precision.
- The only extra time is the memory access of 4 more bytes; negligible in
- comparison to the calculation time.
-
- mem4r is a 4-byte quantity (typically defined with a DD directive) containing
- a single-precision floating-point number.
-
- mem10d is a 10-byte quantity (also defined via DT) containing a special Binary
- Coded Decimal format recognized by the FBLD and FBSTP instructions. This
- format is useful for input and output of floating-point numbers.
-
- mem4i is a 4-byte quantity representing a signed integer in two's-complement
- notation.
-
- mem2i is a 2-byte quantity representing a signed integer in two's-complement
- notation.
-
- mem14 and mem94 are 14- and 94-byte buffers containing the 87 machine state.
-
-
- Operand Choices in A86
-
- The choice of operands for floating-point instructions seems inconsistent to me.
- For example, to subtract stack i from 0, you must provide two operands; to do
- the equivalent comparison, you must provide only one operand. A86 smooths out
- these inconsistencies by allowing more choices for operands: FADD i is
- equivalent to FADD 0,i. FCOM 0,i is equivalent to FCOM i. The same holds for
- the other main arithmetic instructions. FXCH 0,i and FXCH i,0 are allowed.
- So if you wish to retain compatibility with other assemblers, you should use
- their more restrictive instruction list, not the following one.
-
-
- The 87 Instruction Set
-
- Following is the 87 instruction set. The "w" in the opcode field is the FWAIT
- opcode, hex 9B, which is suppressed if .287 is selected. Again, "0", "1", amd
- "i" stand for the associated floating point stack registers, not constant
- numbers! Constant numbers in the descriptions are given with decimal points:
- 0.0, 1.0, 2.0, 10.0.
-
- w D9 F0 F2XM1 0 := (2.0 ** 0) - 1
- w D9 E1 FABS 0 := |0|
- w DE C1 FADD 0 := 1 + 0, pop
- w D8 C0+i FADD i 0 := i + 0
- w DC C0+i FADD i,0 i := i + 0
- w D8 C0+i FADD 0,i 0 := i + 0
- w D8 /0 FADD mem4r 0 := 0 + mem4r
- w DC /0 FADD mem8r 0 := 0 + mem8r
- w DE C0+i FADDP i,0 i := i + 0, pop
- w DF /4 FBLD mem10d push, 0 := mem10d
- w DF /6 FBSTP mem10d mem10d := 0, pop
-
- w D9 E0 FCHS 0 := -0
- 9B DB E2 FCLEX clear exceptions
- w D8 D1 FCOM compare 0 - 1
- w D8 D0+i FCOM 0,i compare 0 - i
- w D8 D0+i FCOM i compare 0 - i
- w D8 /2 FCOM mem4r compare 0 - mem4r
- w DC /2 FCOM mem8r compare 0 - mem8r
- w D8 D9 FCOMP compare 0 - 1, pop
- w D8 D8+i FCOMP 0,i compare 0 - i, pop
- w D8 D8+i FCOMP i compare 0 - i, pop
- w D8 /3 FCOMP mem4r compare 0 - mem4r, pop
- w DC /3 FCOMP mem8r compare 0 - mem8r, pop
- w DE D9 FCOMPP compare 0 - 1, pop both
- w D9 F6 FDECSTP decrement stack pointer
- w DB E1 FDISI disable interrupts (.287 ignore)
- w DE F9 FDIV 1 := 1 / 0, pop
- w D8 F0+i FDIV i 0 := 0 / i
- w DC F8+i FDIV i,0 i := i / 0
- w D8 F0+i FDIV 0,i 0 := 0 / i
- w D8 /6 FDIV mem4r 0 := 0 / mem4r
- w DC /6 FDIV mem8r 0 := 0 / mem8r
- w DE F8+i FDIVP i,0 i := i / 0, pop
- w DE F1 FDIVR 1 := 0 / 1, pop
- w D8 F8+i FDIVR i 0 := i / 0
- w DC F0+i FDIVR i,0 i := 0 / i
- w D8 F8+i FDIVR 0,i 0 := i / 0
- w D8 /7 FDIVR mem4r 0 := mem4r / 0
- w DC /7 FDIVR mem8r 0 := mem8r / 0
- w DE F0+i FDIVRP i,0 i := 0 / i, pop
- w DB E0 FENI enable interrupts (.287 ignore)
- w DD C0+i FFREE i empty i
- w DE /0 FIADD mem2i 0 := 0 + mem4i
- w DA /0 FIADD mem4i 0 := 0 + mem2i
- w DE /2 FICOM mem2i compare 0 - mem2i
- w DA /2 FICOM mem4i compare 0 - mem4i
- w DE /3 FICOMP mem2i compare 0 - mem2i, pop
- w DA /3 FICOMP mem4i compare 0 - mem4i, pop
- w DE /6 FIDIV mem2i 0 := 0 / mem2i
- w DA /6 FIDIV mem4i 0 := 0 / mem4i
- w DE /7 FIDIVR mem2i 0 := mem2i / 0
- w DA /7 FIDIVR mem4i 0 := mem4i / 0
- w DF /0 FILD mem2i push, 0 := mem2i
- w DB /0 FILD mem4i push, 0 := mem4i
- w DF /5 FILD mem8i push, 0 := mem8i
- w DE /1 FIMUL mem2i 0 := 0 * mem2i
- w DA /1 FIMUL mem4i 0 := 0 * mem4i
- w D9 F7 FINCSTP increment stack pointer
- 9B DB E3 FINIT initialize 80287
- w DF /2 FIST mem2i mem2i := 0
- w DB /2 FIST mem4i mem4i := 0
- w DF /3 FISTP mem2i mem2i := 0, pop
- w DB /3 FISTP mem4i mem4i := 0, pop
- w DF /7 FISTP mem8i mem8i := 0, pop
- w DE /4 FISUB mem2i 0 := 0 - mem2i
- w DA /4 FISUB mem4i 0 := 0 - mem4i
- w DE /5 FISUBR mem2i 0 := mem2i - 0
- w DA /5 FISUBR mem4i 0 := mem4i - 0
-
- w D9 C0+i FLD i push, 0 := old i
- w DB /5 FLD mem10r push, 0 := mem10r
- w D9 /0 FLD mem4r push, 0 := mem4r
- w DD /0 FLD mem8r push, 0 := mem8r
- w D9 E8 FLD1 push, 0 := 1.0
- w D9 /5 FLDCW mem2i control word := mem2i
- w D9 /4 FLDENV mem14 environment := mem14
- w D9 EA FLDL2E push, 0 := log base 2.0 of e
- w D9 E9 FLDL2T push, 0 := log base 2.0 of 10.0
- w D9 EC FLDLG2 push, 0 := log base 10.0 of 2.0
- w D9 ED FLDLN2 push, 0 := log base e of 2.0
- w D9 EB FLDPI push, 0 := Pi
- w D9 EE FLDZ push, 0 := +0.0
- w DE C9 FMUL 1 := 1 * 0, pop
- w D8 C8+i FMUL i 0 := 0 * i
- w DC C8+i FMUL i,0 i := i : 0
- w D8 C8+i FMUL 0,i 0 := 0 * i
- w D8 /1 FMUL mem4r 0 := 0 * mem4r
- w DC /1 FMUL mem8r 0 := 0 * mem8r
- w DE C8+i FMULP i,0 i := i * 0, pop
- DB E2 FNCLEX nowait clear exceptions
- DB E1 FNDISI disable interrupts (.287 ignore)
- DB E0 FNENI enable interrupts (.287 ignore)
- DB E3 FNINIT nowait initialize 80287
- w D9 D0 FNOP no operation
- DD /6 FNSAVE mem94 mem94 := 80287 state
- D9 /7 FNSTCW mem2i mem2i := control word
- D9 /6 FNSTENV mem14 mem14 := environment
- DF E0 FNSTSW AX AX := status word
- DD /7 FNSTSW mem2i mem2i := status word
- w D9 F3 FPATAN 0 := arctan(1/0), pop
- w D9 F8 FPREM 0 := REPEAT(0 - 1)
- w D9 F2 FPTAN push, 1/0 := tan(old 0)
- w D9 FC FRNDINT 0 := round(0)
- w DD /4 FRSTOR mem94 80287 state := mem94
- w DD /6 FSAVE mem94 mem94 := 80287 state
- w D9 FD FSCALE 0 := 0 * 2.0 ** 1
- 9B DB E4 FSETPM set protection mode
- w D9 FA FSQRT 0 := square root of 0
- w DD D0+i FST i i := 0
- w D9 /2 FST mem4r mem4r := 0
- w DD /2 FST mem8r mem8r := 0
- w D9 /7 FSTCW mem2i mem2i := control word
- w D9 /6 FSTENV mem14 mem14 := environment
- w DD D8+i FSTP i i := 0, pop
- w DB /7 FSTP mem10r mem10r := 0, pop
- w D9 /3 FSTP mem4r mem4r := 0, pop
- w DD /3 FSTP mem8r mem8r := 0, pop
- w DF E0 FSTSW AX AX := status word
- w DD /7 FSTSW mem2i mem2i := status word
- w DE E9 FSUB 1 := 1 - 0, pop
- w D8 E0+i FSUB i 0 := 0 - i
- w DC E8+i FSUB i,0 i := i - 0
- w D8 E0+i FSUB 0,i 0 := 0 - i
- w D8 /4 FSUB mem4r 0 := 0 - mem4r
- w DC /4 FSUB mem8r 0 := 0 - mem8r
-
- w DE E8+i FSUBP i,0 i := i - 0
- w DE E1 FSUBR 1 := 0 - 1, pop
- w D8 E8+i FSUBR i 0 := i - 0
- w DC E0+i FSUBR i,0 i := 0 - i
- w D8 E8+i FSUBR 0,i 0 := i - 0
- w D8 /5 FSUBR mem4r 0 := mem4r - 0
- w DC /5 FSUBR mem8r 0 := mem8r - 0
- w DE E0+i FSUBRP i,0 i := 0 - i, pop
- w D9 E4 FTST compare 0 - 0.0
- 9B FWAIT wait for 80287 ready
- w D9 E5 FXAM C3 -- C0 := type of 0
- w D9 C9 FXCH exchange 0 and 1
- w D9 C8+i FXCH 0,i exchange 0 and i
- w D9 C8+i FXCH i exchange 0 and i
- w D9 C8+i FXCH i,0 exchange 0 and i
- w D9 F4 FXTRACT push, 1 := expo, 0 := sig
- w D9 F1 FYL2X 0 := 1 * log base 2.0 of 0, pop
- w D9 F9 FYL2XP1 0 := 1 * log base 2.0 of (0+1.0), pop
-